#include "inc/main_color.h"
#include "broot_force.h"
#include <fstream>
#include <iostream>
#include <vector>
#include <boost/tuple/tuple.hpp>
#undef assert
#include "assert.h"

const size_t START_COLORING_POTENCIAL = 2200;

using std::cerr;
using std::auto_ptr;
using std::endl;
using std::vector;
using std::string;
using std::min;

void BrootForce::createCheckers()
{
    finalCheckers.push_back(new FinalSymmetricChecker());
    finalCheckers.push_back(new FinalDimensionChecker());
    finalCheckers.push_back(new FinalBiparateChecker());
    finalCheckers.push_back(new FinalNaiveColoringChecker());
}

void BrootForce::createModifiers()
{
    CombineChecker* vertexModifierChecker = new CombineChecker();
    vertexModifierChecker->addChecker(new FinalSymmetricChecker());
    vertexModifierChecker->addChecker(new OnlineSymmetricCoordChecker());

    modifiers.push_back(new VertexModifier(vertexModifierChecker));
    modifiers.push_back(new DegreeModifier());
//    modifiers.push_back(new DimensionModifier());
    modifiers.push_back(new ColoringModifier());
}

string fname()
{
    static int x = 0;
    char res[100];
    sprintf(res, "tmp/%.4d", x++);
    return res;
}

void BrootForce::run(size_t v)
{
    while (v < NUM_VERTICES && state.getted[v] == 0) {
        statusBar.breakBrunch(v);
        ++v;
    }    
    
    if (v >= NUM_VERTICES) {
        statusBar.breakBrunch(v);
        Graph g(state.dist, state.dist,
            vector<size_t>(state.vertecies,
                           state.vertecies + state.numVertecies));
        state.subGraph = &g;
        for(size_t i = 0; i < finalCheckers.size(); ++i) {
            if (!finalCheckers[i].check(state)) {
                return ;
            }
        }
        string f = fname();
        draw(&g, f);
        if (f.substr(f.length() - 4) == "0000") cerr << f << endl;
        //exit(0);
        state.subGraph = 0;
        return ;
    }
    if (state.numPotencialVertecies < START_COLORING_POTENCIAL
        && state.numVertecies >= START_COLORING_GETTED
        ) {
        vector<size_t> verts;
        for(size_t i = 0; i < NUM_VERTICES; ++i) {
            if (state.getted[i] != 0) {
                verts.push_back(i);
            }
        }
        Graph g(state.dist, state.dist, verts);
//        cerr << verts.size() << " " << state.numPotencialVertecies << endl;
        assert(verts.size() == state.numPotencialVertecies, "numPotencialVertecies is incorrect");
        string f = fname();
        //if (f.substr(f.length() - 3) == "000") {
            cerr << f << " " << state.numPotencialVertecies << " == " << state.numVertecies << endl;
        //}
        /*std::ofstream out(f.c_str());
        printInDIMACS(&g, out);
        out.close();*/
        GraphC* converted = convertToGraphC(&g);
     	char fnamechar[100];
        strcpy(fnamechar,f.data());
        bool colored = color(fnamechar, n + 1, converted);
        destructGraph(converted);
	 	if (colored) {
	        cerr << "succesfully colored" << endl;
            statusBar.breakBrunch(v);
            ++successColored;
            if (successColored > 4 * unsuccessColored
                    && START_COLORING_GETTED > 2) {
                --START_COLORING_GETTED;
                successColored = unsuccessColored = 30;
            }
	        return;
        } else {
            ++unsuccessColored;
            if (unsuccessColored > 4 * successColored) {
                ++START_COLORING_GETTED;
                successColored = unsuccessColored = 30;
            }
        }
    }
    UndoStack undoStack;
    if (processEvent(Event(GET_VERTEX, v), undoStack)) {
        run(v + 1);
    } else {
         statusBar.breakBrunch(v);
    }
    processUndoStack(undoStack);

    if (processEvent(Event(NOT_GET_VERTEX, v), undoStack)) {
        run(v + 1);
    } else {
        statusBar.breakBrunch(v);
    }
    processUndoStack(undoStack);
}

bool BrootForce::processEvent(Event event, UndoStack& undoStack)
{
    EventQueue eventQueue;
    eventQueue.push(event);
    return processEventQueue(eventQueue, undoStack);
}

bool BrootForce::processEventQueue(EventQueue& eventQueue, UndoStack& undoStack)
{
    while (!eventQueue.empty()) {
        Event event = eventQueue.front();
        eventQueue.pop();
        size_t lastModifier = runModifiers(event, modifiers.size(), eventQueue);
        undoStack.push(
            std::make_pair(Event(Event::undoType(event.type), event.vertex),
                      lastModifier));
        if (lastModifier != modifiers.size()) {
            return false;
        }
    }
    return true;
}

bool BrootForce::processUndoStack(UndoStack& undoStack)
{
    bool res = true;
    EventQueue eventQueue;
    while (!undoStack.empty()) {
        Event event;
        size_t lastModifier;
        boost::tie(event, lastModifier) = undoStack.top();
        undoStack.pop();
        runModifiers(event, lastModifier, eventQueue);
    }
    assert(eventQueue.empty(), "eventQueue.empty()");
    return res;
}

size_t BrootForce::runModifiers(Event event,
                                size_t lastModifier,
                                EventQueue& eventQueue)
{
    size_t v = event.vertex;
    for(size_t i = 0; i < min(modifiers.size(), lastModifier + 1); ++i) {
        IModifier& modifier = modifiers[i];
        switch (event.type) {
            case GET_VERTEX:
                if (!modifier.getVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            case UNDO_GET_VERTEX:
                if (!modifier.undoGetVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            case NOT_GET_VERTEX:
                if (!modifier.notGetVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            case UNDO_NOT_GET_VERTEX:
                if (!modifier.undoNotGetVertex(state, v, eventQueue)) {
                    return i;
                }
                break;
            default:
                assert(false, "???");
        }
    }
    return modifiers.size();
}
